07 de Junio, 2017

Contenidos

1. Introducción

1. Introducción

Hasta el momento hemos hablado de la creación de documentos mediante R Markdown, interactivos o no, pensando en un uso local.

Estamos en la sociedad de la información e internet lo domina todo. ¿Podemos utilizar internet para ayudar en la investigación reproducible?

  • Shiny es un paquete de R que nos permite desarrollar aplicaciones web interactivas.
    • Esta siendo desarrollado por los creadores de RStudio, destacan especialmente Winston Chang, Joe Cheng, JJ Allaire, Yihui Xie, Jonathan McPherson, …
    • No necesita conocimientos de lenguajes de programación como HTML, CSS o JavaScript.

2. Motivación

Ejemplos

El papel de Shiny en la Investigación Reproducible

Shiny es una herramienta perfecta para compartir nuestros estudios, permitiendo el acceso a la información. Esto genera numerosos beneficios:

  • Mayor visibilidad de nuestro trabajo (mayor control)
  • Transparencia
  • Extensión a artículos científicos (apéndices más actuales)
    • Cada vez más revistas piden acceso al código y los datos
  • Complemento a paquetes de R
  • Objetivo: Aprender a crear aplicaciones web utilizando Shiny.

3. Empezando a caminar con Shiny

Instalación de Shiny y creación de la primera app

Paso 1. Abrimos RStudio.

Paso 2. Instalamos el paquete "shiny" desde CRAN (en caso de no haberlo instalado previamente):

install.packages("shiny", dependencies = TRUE)

Instalación de Shiny y creación de la primera app

Paso 3. Seleccionamos File >> New File >> Shiny Web App….

Instalación de Shiny y creación de la primera app

Paso 4. Indicamos el nombre de la aplicación, el número de archivos que queremos y el directorio de creación.

  1. Single File: Se crea una carpeta con el nombre de la aplicación en el directorio indicado. Esta carpeta contendrá el archivo app.R.
  2. Multiple File: Se crea una carpeta con el nombre de la aplicación en el directorio indicado. Esta carpeta contendrá los archivos ui.R y server.R.

Ejemplo Shiny >> Single File (app.R)

Ejemplo Shiny >> Multiple File (ui.R/server.R)


Ejemplo Shiny >> Cerrar la app

Si no cerramos la app, esta se queda ejecutándose en R y no podremos seguir trabajando.

4. Componentes básicas de una aplicación Shiny

4. Componentes básicas de una aplicación Shiny

Para crear una aplicación Shiny es suficiente con tener los scripts ui.R y server.R (app.R) dentro de la misma carpeta. El nombre de estos scripts no se puede cambiar.

La estructura es la siguiente:

  • Carperta nombre_app:
    • ui.R y server.R (app.R)


  • Podemos añadir más archivos (.R, .RData, .jpg, .css, …) que serán ejecutados desde ui.R o server.R.

User interface (ui / ui.R)

ui / ui.R (User interface)

  • La construcción de la interfaz de usuario es como montar un puzzle.


1. Estructura general (layouts de página)

2. División de la página (layouts)

3. Contenido de la página (widgets)

User interface

Layouts

Layouts: ¿Cómo diseñamos nuestra UI?

  • Estructura general
    Las funciones que definen la estructura general de la página suelen tener nombres ...Page().

  • División de la página
    Las funciones para definir el diseño de la página suelen tener nombres ...Layout().

  • División de la página por paneles
    Las funciones para definir paneles en la página suelen tener nombres ...Panel().

Para el diseño debemos pensar donde se va a reproducir nuestra aplicación:

  • web (¿tamaño de la pantalla fijo?)
  • tablet, móvil (smartphone), smartwatch, …

Layout para página: fluidPage()

Página en blanco, default. La mayoría de aplicaciones deben utilizar fluidPage() en combinación con otros Layouts.

ui <- fluidPage(
  
  titlePanel("Shiny - fluidPage")
  
)

server <- function(input, output) {
}

shinyApp(ui, server)
fluidPage(..., title = NULL, responsive = NULL, theme = NULL)

Layout para página: bootstrapPage()

Página en blanco. bootstrapPage() está pensada para usuarios que tienen dominio en HTML / CSS y saben diseñar webs con Bootstrap.

ui <- bootstrapPage(
  
  titlePanel("Shiny - bootstrapPage")
  
)

server <- function(input, output) {
}

shinyApp(ui, server)

Layouts

sidebarLayout()

verticalLayout()

splitLayout()

flowLayout()

Layouts

Notas:

  • Los elementos de la página se definen de arriba abajo.

  • La separación entre los elementos es con comas.

  • Los layouts se pueden combinar unos dentro de otros, anidados.

  • Gran flexibilidad en la configuración del UI.

Layout: sidebarLayout()

Página con barra lateral. Usualmente se emplea para colocar selectores que manipulará el usuario y darán como respuesta una salida en el panel principal.

ui <- fluidPage(
  titlePanel("Shiny - sidebarLayout"),
  sidebarLayout(
    sidebarPanel(
      sliderInput("obs", "Número de observaciones:", min = 0, max = 1000, value = 500)
    ),
    mainPanel(
      plotOutput("distPlot")
)))
server <- function(input, output) {
  output$distPlot <- renderPlot({
    hist(rnorm(input$obs))
})}
shinyApp(ui, server)

Layout: verticalLayout()

Página dividida verticalmente que permite introducir contenido por filas.

ui <- fluidPage(
  titlePanel("Shiny - verticalLayout"),
  verticalLayout(
    a(href="http://example.com/link1", "Link Primero"),
    a(href="http://example.com/link2", "Link Segundo"),
    a(href="http://example.com/link3", "Link Tercero")
  )
)
server <- function(input, output) {}
shinyApp(ui, server)

Layout: column() / fluidRow()

Definimos una columna de 12 unidades de dimensión. Nos permite asegurar la posición, horizontalmente, de los elementos definidos en ella.

  • fluidRow() nos permite asegurar que los elementos definidos están en la misma fila. Usualmente su utilización viene combinada con la de column().
ui <- fluidPage(
  titlePanel("Shiny - column() / fluidRow()"),
  fluidRow(
    column(width = 4, wellPanel("Columna de 4 unidades")),
    column(width = 3, offset = 2, wellPanel("Columna de 3 unidades"))
))
shinyApp(ui, server = function(input, output) { })

Layout: splitLayout()

Dividimos la página en dos partes, marcadas por cellWidths.

ui <- fluidPage(
  titlePanel("Shiny - splitLayout"),
  splitLayout(cellWidths = c("50%", "50%"),
    plotOutput("plot1"),
    plotOutput("plot2")
  )
)
server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}
shinyApp(ui, server)

Layout: conditionalPanel()

Definimos un panel condicional, que aparecerá dependiendo de que se cumpla cierta condición.

ui <- fluidPage(titlePanel("Shiny - conditionalPanel"),
  sidebarPanel(
   selectInput("plotType", "Plot Type", c(Scatter = "scatter", Histogram = "hist")),
   conditionalPanel(condition = "input.plotType == 'hist'",
      selectInput("breaks", "Breaks", c("Sturges", "[Custom]" = "custom")),
      conditionalPanel(condition = "input.breaks == 'custom'",
         sliderInput("breakCount", "Break Count", min = 1, max = 1000, value = 10)
      )
   )
  )
)
server <- function(input, output) {}
shinyApp(ui, server)

Si condicionamos por una variable definida por el usuario:
- Nos referiremos a la variable como input.xxx, NO usaremos input$xxx.

Layer Paneles: navbarPage()

Crea una página que tiene una barra de navegación en la parte superior

ui <- navbarPage("Shiny - navbarPage",
  tabPanel("Primera Página", plotOutput("plot1")),
  tabPanel("Segunda Página", plotOutput("plot2"))
)
server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}
shinyApp(ui, server)
navbarPage(title, ..., id = NULL, selected = NULL, position = c("static-top", "fixed-top", "fixed-bottom"), header = NULL, footer = NULL, inverse = FALSE, collapsible = FALSE, collapsable, fluid = TRUE, responsive = NULL, theme = NULL, windowTitle = title)

Layer Paneles: navbarMenu()

Página igual a navbarPage() pero permite embeber un menu en la barra de navegación.

ui <- navbarPage("Shiny - navbarMenu",
  tabPanel("Primera Página"),
  navbarMenu("Gráficos",
    tabPanel("Gráfico 1", plotOutput("plot1")),
    tabPanel("Gráfico 2", plotOutput("plot2")))
)
server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}
shinyApp(ui, server)

Layer Paneles: tabsetPanel()

Permite crear diferentes pestañas con tabPanel(), cada una de ellas permitirá mostrar vistas independientes.

ui <- fluidPage(
  titlePanel("Shiny - tabsetPanel"),
  mainPanel(
    tabsetPanel(
      tabPanel("Plot 1", plotOutput("plot1")),
      tabPanel("Plot 2", plotOutput("plot2"))
    )
  )
)
server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}
shinyApp(ui, server)

Layer Paneles: navlistPanel()

Genera una barra lateral de navegación con pestañas.

ui <- fluidPage(
  titlePanel("Shiny - navlistPanel"),
  navlistPanel("PANELES",
    tabPanel("Primer Panel", plotOutput("plot1")),
    tabPanel("Segundo Panel", plotOutput("plot2"))
  )
)
server <- function(input, output) {
  output$plot1 <- renderPlot(plot(cars))
  output$plot2 <- renderPlot(plot(pressure))
}
shinyApp(ui, server)

User interface

Widgets: Inputs

Widgets: Inputs

Control widgets

Server

server / server.R

¿Qué hacemos en el server.R?

En el server es donde debemos realizaremos los cálculos, tablas y gráficos que queremos mostrar en el UI.

  • Sin orden. Los elementos del server no tienen que seguir un orden de aparición (a diferencia de los del UI).

  • Funciones. Cada acción o grupo de acciones relacionadas, debe ser definida en una función.

  • Programación modular. El código que creamos en nuestros scripts ahora debe ir en el server.R.

Server

Widgets: Outputs

Widgets: Outputs

Los objetos que generemos en el server.R se definen como output$xxx donde xxx es el nombre del objeto.

Para insertar los objetos creados en el server.R en el ui utilizaremos las funciones ...Output('xxx')

## Código en el server.R
output$hist <- renderPlot({
  hist(rnorm(input$N))
})

## Código en el ui.R
plotOutput("hist")

Al referirnos a variables introducidas por el usuario mediante widgets definidos en el UI, emplearemos input$xxx, siendo xxx es el nombre indicado en el argumento InputID del widget correspondiente.

Server

Reactividad

Utilización de expresiones reactivas

  • Shiny responde de forma inmediata a los cambios introducidos por el usuario pero, ¿siempre queremos esto?

  • En ocasiones nos interesará realizar un cálculo o un gráfico que dependa de más de una variable, ¿que sucedería si permitimos que Shiny nos de una respuesta instantáneamente?

Funciones reactivas

Salida por pantalla >> render*()

  • Las funciones render*() crean una salida para mostrar por pantalla.

  • Los resultados de estas funciones siempre se guardan en output$xxx.

output$hist <- renderPlot({
  hist(rnorm(input$N))
})

Código modular >> reactive()

  • Las expresiones reactive() generan un objeto para ser utilizado. Este objeto cambiará de valor cada vez que se modifique algún input$xxx de su interior.

  • El objeto generado se llama como una función.

data <- reactive({
    rnorm(input$N)
})

output$hist <- renderPlot({
  hist(data())
})
  • Las funciones reactive nos permiten una programación modular. Esto ayuda a que la ejecución de las apps sea más rápida y fluida

Prevenir reacciones >> isolate()

  • isolate() hace que un objeto no sea reactivo.
data <- reactive({
    rnorm(input$N)
})

output$hist <- renderPlot({
  hist(data(),
       main = isolate(input$titulo))
})

Activación de código >> observeEvent()

  • Activa el código para que se ejecute en el servidor.
ui <- fluidPage(
  actionButton(inputId = "accion",
    label = "Acción")
)
server <- function(input, output) {
  observeEvent(input$accion, {
    print(runif(1, 1, 100))
})
}
shinyApp(ui = ui, server = server)

observe()

  • Se comporta como el observeEvent() pero reaccionando a todos los valores reactivos que contiene.

Retrasar reacciones >> eventReactive()

  • Expresión reactiva que solo responden a un valor específico.
ui <- fluidPage(
  numericInput(inputId = "N", label = "Generar número aleatorio entre 0 y:", value = 100, min = 0, max = 300),
  actionButton(inputId = "actualizar",
    label = "Actualizar"),
  textOutput("rnd")
)
server <- function(input, output) {
  data <- eventReactive(input$actualizar, {
    runif(1, 1, input$N)
  })
  
  output$rnd <- renderText({data()})
}
shinyApp(ui = ui, server = server)
  • Podemos emplear eventReactive() para retrasar la ejecución de determinadas reacciones.

reactiveValues()

  • reactiveValues() crea una lista de valores reactivos.

-Estos valores reactivos pueden manejarse (usualmente mediante observeEvent())

ui <- fluidPage(
  actionButton(inputId = "norm", label = "Normal"),
  actionButton(inputId = "unif", label = "Uniforme"),
  plotOutput("hist")
)
server <- function(input, output) {
  rv <- reactiveValues(data = rnorm(100))
  observeEvent(input$norm, { rv$data <- rnorm(100) })
  observeEvent(input$unif, { rv$data <- runif(100) })
  output$hist <- renderPlot({
    hist(rv$data)
}) }
shinyApp(ui = ui, server = server)

Bibliografía y recursos